/* confAdmADT.c */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "confAdmADT.h"

#define INIT_SIZE	2
#define BUFFER_SIZE	256

typedef enum {INI, COMMENT, KEY, VALUE, NUMBER_OF_STATES} stateT;

/* Variable que controla singleton */
static confAdmADT aux = NULL;

typedef struct
{
	char * key;
	char * value;
} propertyT;

struct confAdmCDT
{
	propertyT * prop;
	int n;
	int dim;
	int calls;
};

static void Grow(confAdmADT conf);
static void Error(const char * msg);
static void parseLine(confAdmADT conf, char * buffer);
static void deleteSpaces(char * word);

confAdmADT
NewConfAdm(void)
{
	if (aux != NULL)
	{
		aux->calls++;
		return aux;
	}

	if ( (aux = malloc(sizeof(struct confAdmCDT))) == NULL)
		Error("No memory available\n");
	
	aux->dim = INIT_SIZE;
	aux->n = 0;	
	aux->calls = 1;
	if ( (aux->prop = malloc(aux->dim * sizeof(propertyT))) == NULL)
	{
		free(aux);
		Error("No memory available\n");
	}
	
	return aux;
}

void freeConfAdm(confAdmADT conf)
{
	int i;	

	if (conf->calls == 1)
	{	
		for (i = 0; i < conf->n; i++)
		{
			free(conf->prop[i].key);
			free(conf->prop[i].value);
		}
		free(conf->prop);
		free(conf);
		aux = NULL;
	}
	else
		conf->calls--;
}

int
initConfAdm(confAdmADT conf, char * filename)
{
	char buffer[BUFFER_SIZE];
	FILE * pf;

	if ((pf = fopen(filename, "rt")) == NULL)
		return FALSE;
	
	while (fgets(buffer, BUFFER_SIZE, pf) != NULL)
		parseLine(conf, buffer);
	
	fclose(pf);
	return TRUE;
}

int
saveConfAdm(confAdmADT conf, char * filename)
{
	int i;
	FILE * file;

	if ((file = fopen(filename, "wb")) == NULL)
		return FALSE;	
	
	for (i = 0; i < conf->n; i++)
		fprintf(file,"%s = %s\n", conf->prop[i].key, conf->prop[i].value);
		
	fclose(file);
	return TRUE;
}

int
existKey(confAdmADT conf, char * key)
{
	int i, resp = FALSE;

	for (i = 0; i < conf->n && !resp; i++)
		if (strcmp(key, conf->prop[i].key) == 0)
			resp = TRUE;

	return resp;
}	

int
findValue(confAdmADT conf, char * key, char ** resp)
{
	int i, ans = FALSE;

	for (i = 0; i < conf->n && !ans; i++)
		if (strcmp(key, conf->prop[i].key) == 0)
		{
			if ((*resp = malloc(strlen(conf->prop[i].value) + 1)) == NULL)
				Error("No memory available\n");
			strcpy(*resp, conf->prop[i].value);
			ans = TRUE;
		}

	return ans;
}

int
addProperty(confAdmADT conf, char * key, char * value)
{
	if (existKey(conf, key))
		return FALSE;

	Grow(conf);

	propertyT * new;

	if ((new = malloc(sizeof(propertyT))) == NULL)
		return FALSE;
	if ((new->key = malloc(strlen(key) + 1)) == NULL)
		return FALSE;
	if ((new->value = malloc(strlen(value) + 1)) == NULL)
		return FALSE;

	strcpy(new->key, key);
	strcpy(new->value, value);
	conf->prop[conf->n] = *new;

	conf->n++;
	
	free(new);
	return TRUE;
}	

int
updateProperty(confAdmADT conf, char * key, char * value)
{
	int i, ans = FALSE;
	
	if (!existKey(conf, key))
		return FALSE;

	for (i = 0; i < conf->n && !ans; i++)
		if (strcmp(key, conf->prop[i].key) == 0)
		{
			free(conf->prop[i].value);
			if ((conf->prop[i].value = malloc(strlen(value) + 1)) == NULL)
				Error("No memory available\n");

			strcpy(conf->prop[i].value, value);
			ans = TRUE;
		}

	return ans;
}

void 
printConfAdm(confAdmADT conf)
{
	int i;
	
	for (i = 0; i < conf->n; i++)
		printf("%s = %s\n", conf->prop[i].key, conf->prop[i].value);

	return;
}

/* FUNCIONES AUXILIARES */

static void
parseLine(confAdmADT conf, char * buffer)
{
	char keyBuffer[MAX_KEY_LEN];
	char valueBuffer[MAX_VALUE_LEN];
	int iKey, iValue;	
	int newProp = FALSE;

	iKey = iValue = 0;
	stateT state = INI;

	while (*buffer != '\0')
	{
		switch(state)
		{
		case INI: 	if (isspace(*buffer))
					while (isspace(*buffer))
						buffer++;
				else if (*buffer == '#')
					return;
				else
				{
					state = KEY;
					keyBuffer[iKey++] = *buffer++;
				}
				break;
		case COMMENT:	return;
		case KEY:	if (*buffer == '\n' || *buffer == '#')
					return;
				else if (*buffer == '=')
				{
					state = VALUE;
					keyBuffer[iKey] = '\0';
					buffer++;
				}
				else
					keyBuffer[iKey++] = *buffer++;
				break;
		case VALUE:	if (isspace(*buffer) && iValue == 0)
					while (isspace(*buffer))
						buffer++;
				else if (*buffer == '#' && iValue == 0)
					return;
				else 
				{
					while (*buffer != '\n' && *buffer != '#' && *buffer != '\0')
						valueBuffer[iValue++] = *buffer++;
					valueBuffer[iValue] = '\0';
					while (*buffer != '\0')
						buffer++;
					newProp = TRUE;
				}
				break;
		default:	break;
		}
	}

	if (newProp == TRUE)
	{
		deleteSpaces(keyBuffer);
		deleteSpaces(valueBuffer);
		addProperty(conf, keyBuffer, valueBuffer);
	}

	return;
}

static void 
Grow(confAdmADT conf)
{
	propertyT * pAux;

	if (conf != NULL && conf->prop != NULL)
	{
		if (conf->n == conf->dim)
		{
			if ((pAux = realloc(conf->prop, 
				conf->dim * 2 *sizeof(propertyT))) == NULL)
				Error("No memory available\n");

			conf->prop = pAux;
			conf->dim *= 2;
		}
	}
}

static void
Error(const char * msg)
{
	fprintf(stderr, msg);
	exit(EXIT_FAILURE);
}

static void
deleteSpaces(char * word)
{
	int len;
		
	len = strlen(word);

	if (len > 0)
		while (isspace(word[len - 1]))
			word[--len] = '\0';
	return;
}
